/**
 * DE WORLD PACKAGE
 */
package simulatie;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Random;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
import javax.swing.JFrame;
import javax.vecmath.Vector3f;

import protocol.Cmd;
import protocol.CommSckClient;
import protocol.CommSckClientIn;
import protocol.CommSckClientOut;
import protocol.CommandRelay;
import protocol.Commands;
import simulatie.models.*;
import statter.Stats;

import com.sun.opengl.util.FPSAnimator;

/**
 * De World Klasse, hierin wordt de wereld in elkaar gelsleuteld
 * @author Faceoff / TetraCON
 */
public class World implements GLEventListener {
	private float MOVESPEED = 0.005f; // Wijzig hier de snelheid van beweging
	private float TURNSPEED = 0.05f; // Wijzig hier de snelheid van rotaties
	private Hashtable<Integer, AGV> agvs = new Hashtable<Integer, AGV>();
	private Hashtable<Integer, Kraan> binnenKranen = new Hashtable<Integer, Kraan>();
	private Hashtable<Integer, Kraan> vwKranen = new Hashtable<Integer, Kraan>();
	private Hashtable<Integer, Kraan> treinKranen = new Hashtable<Integer, Kraan>();
	private Hashtable<Integer, Kraan> opslagKranen = new Hashtable<Integer, Kraan>();
	private Hashtable<Integer, Kraan> zvKranen = new Hashtable<Integer, Kraan>();
	
	private Hashtable<Integer, Container> opslag = new Hashtable<Integer, Container>();
	
	private AGV agv;
	private Container cont;
	private Random rndm;
	
	private boolean blendingEnabled;
	private GLCanvas glCanvas;
	private FPSAnimator animator;
	private InputHandler inputHandler;
	private GLU glu;
	private GL gl;
	private int filter;
	private int[] textures = new int[3];
	private Vector3f camPos = new Vector3f(10.0f, 0.45f, 1.0f);
	private Vector3f camView = new Vector3f(10.0f, 1.0f, 120.0f);
	
	int cnt = 0;

	// private Vector3f CamUP = new Vector3f();
	private CommSckClientIn commClientIn;
	private CommSckClientOut commClientOut;
	private CommandRelay commRelay;
	private Cmd command = new Cmd(0,0,0);
	private int count;
	private Stats statter;
	/**
	 * Constructor Maakt de JFrame, linkt de InputHandler en maakt de Canvas.<br>
	 * GLU & Animator
	 * 
	 */
	public World(Stats statter) {
		this.statter = statter;
		glCanvas = new GLCanvas(new GLCapabilities());
		glCanvas.setBounds(0, 0, 1024, 700);

		JFrame frame = new JFrame("Containing Simulator");
		frame.setLayout(null);

		inputHandler = new InputHandler();
		glCanvas.addGLEventListener(this);
		glCanvas.addKeyListener(inputHandler);

		frame.add(glCanvas);
		frame.setSize(800, 600);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);

		glu = new GLU();
		animator = new FPSAnimator(glCanvas, 60);
		animator.setRunAsFastAsPossible(false);
		animator.start();
		
	    // AGV's    
	    agv = new AGV();
	    for (int i = 0; i < 99; i++) {
	    	agv.init();
	    	agvs.put(agvs.size(), (AGV)agv.clone());
	    }
	    
	    rndm = new Random();  
	    cont = new Container();
	    
//	    int n = 0;
//	    for (int i = 0; i <= 5; i++) {
//	    	for (int j = 1; j <= 6; j++) {
//	    		for (int k = 1; k <= 250; k++) {
//	    			n++;
//	    			addContainer(n, i, j, k);
//	    		}
//	    	}
//	    }
//	    System.out.println("" + n);	    
	    
	    setupKranen();
	
		glCanvas.requestFocus();
	}
	
	private void addContainer(int nr, int x, int y, int z) {
		cont.init();
		opslag.put(nr, (Container)cont.clone());
		opslag.get(nr).setPosition(14 + 4.5f*x, 0.11f + 0.25f*y, 6 + 0.21f*z);
		opslag.get(nr).setColor(rndm.nextInt(255), rndm.nextInt(255), rndm.nextInt(255));
	}
	
	private void removeContainer(int nr) {
		opslag.remove(nr);
	}
	
	private void setupKranen() {
	    // Treinkranen	
	    Kraan treinKraan = new Kraan();
	    treinKraan.addRotation(90f, 0, 0, 1);
	    treinKraan.setColor(0, 0, 255);
	    treinKraan.setScale(2f, 8f, 2f);
	    for (int i = 0; i <= 3; i++) {
	    	treinKraan.setPosition(8.5f,0.44f,10 +20*i);
	    	//System.out.println("{" + treinKraan.getPosition().x + "f," + treinKraan.getPosition().y + "f," + treinKraan.getPosition().z + "f},");
	    	treinKranen.put(i, (Kraan)treinKraan.clone ());
	    }
	    
	    //Zeevaartkanen
	    Kraan zvKraan = new Kraan();
	    zvKraan.setColor(255, 255, 0);
	    zvKraan.setScale(2f, 5f, 2f);
	    zvKraan.addRotation(180, 0 ,0, 1);
	    for (int r = 0; r <= 4; r++) {
	    	for (int i = 0; i <= 1; i++) {
	    		zvKraan.setPosition(13 +(i*2) + (r*5), 0.44f, 80f);
	    		zvKranen.put(zvKranen.size(), (Kraan)zvKraan.clone());
	    	}
	    }
	    
	/*    for (int i = 0; i < zvKranen.size(); i++) {
	    	System.out.println("{" + zvKranen.get(i).getPosition().x + "f," + zvKranen.get(i).getPosition().y + "f," + zvKranen.get(i).getPosition().z + "f},");
	    }*/
	    
	    //	  Binnenscheepskranen
	    Kraan binnenKraan = new Kraan();
	    binnenKraan.addRotation(270f, 0, 0, 1);
	    binnenKraan.setColor(255, 0, 0);
	    binnenKraan.setScale(2f, 4.4f, 2f);
	    for (int i = 0; i <= 7; i++) {
	    	binnenKraan.setPosition(39.6f, 0.44f, 77-4*i);
	    	//System.out.println("{" + binnenKraan.getPosition().x + "f," + binnenKraan.getPosition().y + "f," + binnenKraan.getPosition().z + "f},");
	    	binnenKranen.put(i, (Kraan)binnenKraan.clone ());
	    }
  
	    //	  Vrachtwagenkranen
	    Kraan vwKraan = new Kraan();
	    vwKraan.setColor(255, 100, 200);
	    vwKraan.setScale(6.4f, 2f, 2f);
	    for (int i = 0; i <= 19; i++) {
	    	vwKraan.setPosition(40.66f,0.44f,39 -2*i);
	    	//System.out.println("{" + vwKraan.getPosition().x + "f," + vwKraan.getPosition().y + "f," + vwKraan.getPosition().z + "f},");
	    	vwKranen.put (i, (Kraan)vwKraan.clone());
	    }
	    
	    // Opslagkranen
	    Kraan opslagKraan = new Kraan();
	    opslagKraan.addRotation(90f, 0, 0, 1);
	    opslagKraan.setColor(255, 255, 0);
	    opslagKraan.setScale(2f, 4.2f, 2f);
	    for (int i = 0; i <= 5; i++) {
	    	opslagKraan.setPosition(35.4f-(i*4.5f),0.44f,5);      
	    	System.out.println("{" + opslagKraan.getPosition().x + "f," + opslagKraan.getPosition().y + "f," + opslagKraan.getPosition().z + "f},");
	    	opslagKranen.put(i, (Kraan)opslagKraan.clone());
	    }
	}

	public void switchFilter() {
		filter = (filter + 1) % 3;
	}

	public void toggleBlending() {
		blendingEnabled = !blendingEnabled;
	}

	/**
	 * 
	 */
	public void reshape(GLAutoDrawable drawable, int x, int y, int width,
			int height) {
		this.gl = drawable.getGL();
		if (height <= 0)
			height = 1;
		final float h = (float) width / (float) height;
		gl.glViewport(0, 0, width, height); // Reset The Current Viewport
		gl.glMatrixMode(GL.GL_PROJECTION); // Select The Projection Matrix
		gl.glLoadIdentity(); // Reset The Projection Matrix
		// Calculate The Aspect Ratio Of The Window
		glu.gluPerspective(45.0f,h,0.1f,100.0f);
		gl.glMatrixMode(GL.GL_MODELVIEW); // Select The Modelview Matrix
		gl.glLoadIdentity(); // Reset The Modelview Matrix
	}

	/**
	 * 
	 */
	public void init(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();
		gl.glShadeModel(GL.GL_SMOOTH); // Enable Smooth Shading
		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
		gl.glClearDepth(1.0f); // Depth Buffer Setup
		gl.glEnable(GL.GL_DEPTH_TEST); // Enables Depth Testing
		gl.glDepthFunc(GL.GL_LEQUAL); // The Type Of Depth Testing To Do
		gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST); // Really
																	// Nice
																	// Perspective
																	// Calculations
	}

	/**
	 * 
	 */
	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged,
			boolean deviceChanged) {

	}

	/**
	 * 
	 */
	public void display(GLAutoDrawable drawable) {
		statter.statStart("Graphics loop");
		update();

		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		gl.glLoadIdentity();
		glu.gluLookAt(camPos.x, camPos.y, camPos.z, camView.x, camView.y,
				camView.z, 0, 1, 0);
		Build_Grid();
		gl.glBindTexture(GL.GL_TEXTURE_2D, textures[filter]);
		
		if (count>100) {
			this.command = commRelay.getInCmd();
			if ( command != null) {
				System.out.println("Sim: " + command.getCommand());				
				handleCommand();
			}
			count = 0;
		}
		count++;
		
		for (int i = 0; i < agvs.size(); i++) {  	 
			if (agvs.get(i).opBestemming() && !agvs.get(i).getstaStil()) {
				System.out.println("agv " + i + "gearriveerd bij " + agvs.get(i).getEindbestemming());
				commRelay.addOutCmd(new Cmd(Commands.OKV,i,agvs.get(i).getEindbestemming()));
				agvs.get(i).staStil();
			}
		}
			
//			if (cnt == 0) {
//				agvd.setBestemming(49); 
//				addContainer(23, 0, 0, 0);
//				cnt++;
//			}
//			else if (cnt == 1){
//				agvd.setBestemming(0);
//				removeContainer(23);
//				cnt++;
//			}
//			else {
//				agvd.idle();
//			}
//		}
		
	    for (int j = 0; j < agvs.size(); j++) {
	        agvs.get(j).displayModel(gl);
	     }
	    for (int j = 0; j < treinKranen.size(); j++) {
	        treinKranen.get(j).displayModel(gl);
		}
	    for (int j = 0; j < binnenKranen.size(); j++) {
	        binnenKranen.get(j).displayModel(gl);
	    }
	    for (int j = 0; j < vwKranen.size(); j++) {
	        vwKranen.get(j).displayModel(gl);
	    }
	    for (int j = 0; j < opslagKranen.size(); j++) {
	        opslagKranen.get(j).displayModel(gl);
	    }
	    for (int j = 0; j < zvKranen.size(); j++) {
	        zvKranen.get(j).displayModel(gl);
	    }
	    
	    Enumeration<Integer> e = opslag.keys();
	    while (e.hasMoreElements()) {
	    	opslag.get(e.nextElement()).displayModel(gl);
	    }
	    statter.statEnd("Graphics loop");
	}

	/**
	 * Bouwt de Grid op het veld
	 */
	public void Build_Grid() {
		BouwPlatform(10, 80, 190, 190, 150, 0, 0); // Bouw Treinplatform
		BouwPlatform(50, 20, 1, 190, 150, 80, 0); // Bouw Zeeschipplatform
		BouwPlatform(10, 40, 150, 1, 150, 40, 40); // Bouw Binnenvaartplatform
		BouwPlatform(10, 40, 150, 190, 1, 0, 40); // Bouw Vrachtwagenplatform
		BouwPlatform(30, 2.5f, 255, 255, 255, 0, 10); // Bouw de weg
		BouwPlatform(2.5f, 75, 255, 255, 255, 2.5f, 10);
		BouwPlatform(2.5f, 75, 255, 255, 255, 2.5f, 37.5f);
		BouwPlatform(30, 2.5f, 255, 255, 255, 77.5f, 10);
		BouwPlatform(25, 75, 200, 150, 200, 2.5f, 12.5f); // Bouw
															// Opslagplatform
	}

	public void BouwPlatform(float breedte, float lengte, int rood, int blauw,
			int groen, float x, float y) {
		for (float i = 0.0f; i <= breedte; i += 0.05f) {
			gl.glBegin(GL.GL_LINES);
			gl.glColor3ub((byte) rood, (byte) blauw, (byte) groen);
			gl.glVertex3f(0, 0, 0);
			gl.glVertex3f(0, 0, 0);
			gl.glVertex3f(i + y, 0, 0 + x);
			gl.glVertex3f(i + y, 0, lengte + x);
			gl.glEnd();
		}
		for (float i = 0.0f; i <= lengte; i += 0.05f) {
			gl.glBegin(GL.GL_LINES);
			gl.glColor3ub((byte) rood, (byte) blauw, (byte) groen);
			gl.glVertex3f(0 + y, 0, i + x);
			gl.glVertex3f(breedte + y, 0, i + x);
			gl.glVertex3f(0, 0, 0);
			gl.glVertex3f(0, 0, 0);
			gl.glEnd();
		}
	}

	/**
	 * Doet de beweging voor- en achteruit
	 * 
	 * @param speed
	 *            De snelheid en richting van de beweging
	 */
	public void camMove(float speed) {
		Vector3f vVector = new Vector3f(); // Get the view vector
		vVector.sub(camView, camPos);
		// forward positive cameraspeed and backward negative -cameraspeed.
		camPos.x = camPos.x + vVector.x * speed;
		camPos.z = camPos.z + vVector.z * speed;
		camView.x = camView.x + vVector.x * speed;
		camView.z = camView.z + vVector.z * speed;
	}

	/**
	 * Doet de beweging naar links en rechts
	 * 
	 * @param speed
	 *            De snelheid en richting van de beweging
	 */
	public void camStrafe(float speed) {
		Vector3f vVector = new Vector3f(); // Get the view vector
		vVector.sub(camView, camPos);
		Vector3f vOrthoVector = new Vector3f(); // Orthogonal vector for the
												// view vector
		vOrthoVector.x = -vVector.z;
		vOrthoVector.z = vVector.x;
		// right positive cameraspeed and left negative -cameraspeed.
		camPos.x = camPos.x + vOrthoVector.x * speed;
		camPos.z = camPos.z + vOrthoVector.z * speed;
		camView.x = camView.x + vOrthoVector.x * speed;
		camView.z = camView.z + vOrthoVector.z * speed;

	}

	/**
	 * Zorgt voor de horizontale draai van de camera (viewport)
	 * 
	 * @param speed
	 *            De snelheid en richting van de beweging
	 */
	public void camRotateH(float speed) {
		Vector3f vVector = new Vector3f(); // Get the view vector
		vVector.sub(camView, camPos);
		// right positive cameraspeed and left negative -cameraspeed.
		camView.z = (float) (camPos.z + Math.sin(speed) * vVector.x + Math
				.cos(speed)
				* vVector.z);
		camView.x = (float) (camPos.x + Math.cos(speed) * vVector.x - Math
				.sin(speed)
				* vVector.z);
	}

	public void camRotateV(float speed) {
		Vector3f vVector = new Vector3f(); // Get the view vector
		vVector.sub(camView, camPos);
		// down positive cameraspeed and up negative -cameraspeed.
		camView.z = (float) (camPos.z + Math.sin(speed) * vVector.y + Math
				.cos(speed)
				* vVector.z);
		camView.y = (float) (camPos.y + Math.cos(speed) * vVector.y - Math
				.sin(speed)
				* vVector.z);
	}

	/**
	 * Update het scherm, en doet de gevraagde beweging
	 */
	public void update() {
		switch (inputHandler.getPressedKey()) {
		case InputHandler.FORWARD:
			camMove(MOVESPEED);
			break;
		case InputHandler.BACKWARD:
			camMove(-MOVESPEED);
			break;
		case InputHandler.RIGHT:
			camStrafe(MOVESPEED);
			break;
		case InputHandler.LEFT:
			camStrafe(-MOVESPEED);
			break;
		case InputHandler.UP:
			if (camPos.y > -2) {
				camPos.y -= MOVESPEED * 10;
				camView.y -= MOVESPEED * 10;
			}
			break;
		case InputHandler.DOWN:
			if (camPos.y < 85) {
				camPos.y += MOVESPEED * 10;
				camView.y += MOVESPEED * 10;
			}
			break;
		case InputHandler.ROT_UP:
			camRotateV(-TURNSPEED);
			break;
		case InputHandler.ROT_DOWN:
			camRotateV(TURNSPEED);
			break;
		case InputHandler.ROT_LEFT:
			camRotateH(-TURNSPEED);
			break;
		case InputHandler.ROT_RIGHT:
			camRotateH(TURNSPEED);
			break;
		case InputHandler.CON_LEFT:
			break;
		case InputHandler.CON_RIGHT:
			break;
		case 0:
			break;
		}
	}

	public void connectServer(String address, int port) {
		commRelay = new CommandRelay();
		commClientIn = new CommSckClientIn(commRelay, statter);
		commClientOut = new CommSckClientOut(commRelay, statter);
		commRelay.addOutCmd(new Cmd(Commands.OK,255,255));
	}

	public void handleCommand() {
		System.out.println("World: handleCommand() " + command.getCommand());
		switch (command.getCommand()) {
		case Commands.STUUR_AGV:
//			commRelay.addOutCmd(new Cmd(Commands.OK,0,0));
			agvs.get(command.getAgvNummer()).setBestemming(command.getKraannr());
			break;
		case Commands.NAAR_OPSLAG:
//			commRelay.addOutCmd(new Cmd(Commands.OK,0,0));
			agvs.get(command.getAgvNummer()).setBestemming(command.getKraannr());
			System.out.println(" Agv " + command.getAgvNummer() + " gaat nu naar " +command.getKraannr());
			break;
/*		case Commands.BINNENKOMST_TREIN:
			commRelay.setOutCmd(Commands.PARAMETERS);
			break;
		case Commands.BINNENKOMST_VRACHTWAGEN:
			commRelay.setOutCmd(Commands.PARAMETERS);
			break;
		case Commands.BINNENKOMST_ZEESCHIP:
			commRelay.setOutCmd(Commands.PARAMETERS);
			break;
		}*/
		}
	}	
}
